package proyectoia;

import aima.search.framework.GoalTest;
import aima.search.framework.HeuristicFunction;
import aima.search.framework.Search;
import aima.search.framework.SuccessorFunction;
import aima.search.informed.SimulatedAnnealingSearch;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
import org.jfree.ui.RefineryUtilities;

public class StagedExperiment3 extends StagedExperiment {

    SuccessorFunction successor;
    Integer initial;
    SimulatedAnnealingSearch search;

    public StagedExperiment3(int seed, int nIterations, SuccessorFunction bestSuccessor, Integer bestInitial,
            Integer steps, Integer stiter, Integer k, Double lambda) {
        super(seed, nIterations);
        numStages = 1;
        successor = bestSuccessor;
        initial = bestInitial;
        search = new SimulatedAnnealingSearch(steps, stiter, k, lambda);
       //search.traceOn();



    }

    @Override
    protected int getNumUsers() {
        return 200;
    }

    @Override
    protected int getNumRequests() {
        return 5;
    }

    @Override
    protected int getNumServers() {
        return 50;
    }

    @Override
    protected int getNumReplications() {
        return 5;
    }

    @Override
    protected DistFileSystemState getInitialSolution(DistFileSystemState state) {
        switch (initial) {
            case GREEDY:
                state.generateGreedySolution();
                break;
            case RANDOM:
                state.generateRandomSolution(seed);
                break;
            case EMPTY:
                state.generateEmptySolution();
                break;
        }
        return state;
    }

    @Override
    protected Search getSearch() {
        return search;
    }

    @Override
    protected Integer getSearchResult() {
        return STATES;
    }
    
    @Override
    protected SuccessorFunction getSuccessor() {
        return successor;
    }

    @Override
    protected GoalTest getGoalTest() {
        return new DistFileSystemGoalTest();
    }

    @Override
    protected HeuristicFunction getHeuristic() {
       return new DistFileSystemHeuristicFunction1();
       //return new DistFileSystemHeuristicFunction2((float)0.4);
    }
    
    
    
    private double getSpeedup(int s) throws Exception {
        DistFileSystemState instep = new DistFileSystemState(initialStates[s].get(0));
        DistFileSystemState lastS = (DistFileSystemState)this.search.getGoalState();
        int initTime = instep.getSlowestServerTime();
        int lastTime = lastS.getSlowestServerTime();
        return (double)initTime/(double)lastTime;
    } 
   
    public void printSAtune(PrintWriter o, int step, int stiter, int k, double lam) throws Exception {
  
        
        for (int s = 0; s < numStages; s++) {
            SummaryStatistics is = new SummaryStatistics();
            for (int i = 0; i < numIterations; i++) {
                is.addValue(initialStates[s].get(i).getSlowestServerTime());
            }
            
           
            
            //System.out.println("Solució inicial per " + stageName(s));
            //System.out.println("El temps del servidor més lent triga de mitja " + (int) is.getMean() + " amb " + (int) is.getStandardDeviation() + " stdev.");
            SummaryStatistics ss = new SummaryStatistics();
            SummaryStatistics rt = new SummaryStatistics();
            
            DistFileSystemState instep = new DistFileSystemState(initialStates[s].get(0));
            DistFileSystemState lastS = (DistFileSystemState)this.search.getGoalState();
            int initTime = instep.getSlowestServerTime();
            int lastTime = lastS.getSlowestServerTime();


            double gain = (double)initTime/(double)lastTime;
            o.println(initTime+" \t "+lastTime+" \t "+gain+" \t "+step+" \t "+stiter+" \t "+k+" \t "+lam);

            //System.out.println(stageDescription(s));
            //System.out.println("El temps del servidor més lent triga de mitja " + (int) ss.getMean() + " amb " + (int) ss.getStandardDeviation() + " stdev, i triga " + (int) rt.getMean() + " milisegonds en calcular-se.");
        }
        //o.close();
    }
    
 
    static LinkedBlockingDeque<Data> totalList = new LinkedBlockingDeque();
    public static void main(String[] args) throws InterruptedException, IOException {
        
        int db = 0;
        
        
        
        
        if (db == 0) {
            SummaryStatistics sOrigT = new SummaryStatistics();
            SummaryStatistics sFinalT = new SummaryStatistics();
            SummaryStatistics sExecT = new SummaryStatistics();
            SummaryStatistics sSpeedup = new SummaryStatistics();
      
            for (int i = 0; i < 10; i++) {
                StagedExperiment3 se3 = new StagedExperiment3(123+i, 1, new DistFileSystemSuccessorFunctionSA1(), StagedExperiment.GREEDY,
                                    20000, 50, 125, 0.00000001);
                se3.initialitze();
                try {
//                    Plotter plotter = new Plotter(totalList, se3.getNumServers());
//                    plotter.pack();
//                    RefineryUtilities.centerFrameOnScreen(plotter);
//                    plotter.setVisible(true); 
//                    Thread t = new Thread (plotter, "plotter" );
//                    t.start();
                    se3.execute();
//                    t.interrupt();
//                    se3.printSlowestServerTime("experiment-3");


                    DistFileSystemState state = (DistFileSystemState)se3.search.getGoalState();                          

                    double origT = se3.initialStates[0].get(0).getSlowestServerTime();
                    double finalT = state.getSlowestServerTime();
                    double ganancia = origT-finalT;
                    double execT = (double)se3.runTimes[0].get(0);
                    double ratioT = ganancia/execT;
                    
                    sOrigT.addValue(origT);
                    sFinalT.addValue(finalT);
                    sExecT.addValue(execT);
                    sSpeedup.addValue(se3.getSpeedup(0));

                    System.out.println("Original time "+origT);
                    System.out.println("Final time    "+finalT);
                    System.out.println("Speedup       "+se3.getSpeedup(0));
                    System.out.println("Gain          "+ganancia);
                    System.out.println("exec time     "+execT);
                    System.out.println("ratioTime     "+ratioT);



                } catch (Exception ex) {
                    Logger.getLogger(StagedExperiment3.class.getName()).log(Level.SEVERE, null, ex);
                }
        }
            double meanOrigT = sOrigT.getMean();
            double meanFinalT = sFinalT.getMean();
            double meanExecT = sExecT.getMean();
            double meanGain = meanOrigT-meanFinalT;
            double meanRatioT = meanGain/meanExecT;
            double meanSpeedup = sSpeedup.getMean();
            
            System.out.println("===================================");
            System.out.println("Original time "+meanOrigT);
            System.out.println("Final time    "+meanFinalT);
            System.out.println("Speedup       "+meanSpeedup);
            System.out.println("Gain          "+meanGain);
            System.out.println("exec time     "+meanExecT);
            System.out.println("ratioTime     "+meanRatioT);
            
            return;
        }
        
        FileWriter outFile = new FileWriter("data/X5-tuningSA.data");
        PrintWriter o = new PrintWriter(outFile);
        o.println("# Tune simualted anealing");
        o.println("TiempoInicial \t TiempoFinal \t  Mejora \t step \t steps \t k \t lam");
        
        
        int firststep = 20000;
        int firststiter = 100;
        int firstK = 1;
        double firstLam = 10;
        
        int laststep = 20000;
        int laststiter = 100;
        int lastK = 100000;
        double lastLam = 1000000000;
        
        double maxSpeedup = 1.0;
        int beststep = firststep;
        int beststiter = firststiter;
        int bestK = firstK;
        double bestLam = firstLam;
        
       
        
        for (int step = firststep; step <= laststep; step+=500){

            for (int steps = firststiter; steps <= laststiter; steps+=100) {

                if (step%steps != 0) continue;
                for (int k = firstK; k <= lastK; k*=5) {
                    for (double l = firstLam; l <= lastLam; l*=10) {
                        Double lambda = 1/l;

                        System.out.println("step="+step+", steps="+steps+", k =" + k + ", lambda=" + lambda);
                        StagedExperiment3 se3 = new StagedExperiment3(123, 1, new DistFileSystemSuccessorFunctionSA1(), StagedExperiment.GREEDY,
                                step, steps, k, lambda);


                        try {
                            se3.initialitze();
//                            Plotter plotter = new Plotter(totalList, se3.getNumServers(), "step= "+step+", steps= "+steps+", k = " + k + ", lambda = " + lambda);
//                            plotter.pack();
//                            RefineryUtilities.centerFrameOnScreen(plotter);
//                            plotter.setVisible(true); 
//                            Thread t = new Thread (plotter, "plotter" );
//                            t.start();
                            se3.execute();
//                            t.interrupt();
                           // se3.printSlowestServerTime("experiment-3");
                            se3.printSAtune(o,step,steps,k,lambda);
                            
                            
                            DistFileSystemState state = (DistFileSystemState)se3.search.getGoalState();
                            System.out.println("Original time "+se3.initialStates[0].get(0).getSlowestServerTime());
                            System.out.println("Final time    "+state.getSlowestServerTime());
                            System.out.println("Speedup       "+se3.getSpeedup(0));
                            
                            if (se3.getSpeedup(0) > maxSpeedup) {
                                maxSpeedup = se3.getSpeedup(0);
                                bestLam = l;
                                bestK = k;
                                beststep = step;
                                beststiter = steps;
                            }
                             System.out.println("#########Best speedup: "+maxSpeedup);
                            System.out.println("==============================================================");
                            
                        } catch (Exception e) {
                            Logger.getLogger(DistFileSystem.class.getName()).log(Level.SEVERE, null, e);
                        }
                        //Thread.sleep(1000);
                    }
                }               
            }
        }
        
        System.err.println("Best Speedup "+maxSpeedup);
        System.out.println("Best step     "+beststep);
        System.out.println("Best steps   "+beststiter);
        System.out.println("Best k       "+bestK);
        System.out.println("Best lamb    "+bestLam);
        
        
        o.println("#Best Speedup "+maxSpeedup);
        o.println("#Best step     "+beststep);
        o.println("#Best steps   "+beststiter);
        o.println("#Best k       "+bestK);
        o.println("#Best lamb    "+bestLam);
       
        o.close();
    }
}